package mmc;

import static org.lwjgl.input.Keyboard.*;
import static org.lwjgl.opengl.GL11.glRotatef;
import static org.lwjgl.opengl.GL11.glTranslatef;
import mmc.action.Action;
import mmc.action.skill.Skill;
import mmc.cell.Block;
import mmc.cell.ClientCellBunch;
import mmc.networking.EndCast;
import mmc.networking.ObjectMoving;
import mmc.networking.TextMessage;
import mmc.objects.RemotePlayer;
import mmc.server.Server;
import mmc.ui.KeyBinding;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

@SuppressWarnings("serial")
public class Player extends MovableEngineObject{
	public float yaw = 0, pitch = 0;
	protected float wvx=0, wvy=0, wvz=0;
	protected float walkSpeed = 4;
	protected float jumpSpeed = 9;
	protected float mouseSensitivity = 0.35f;
	public final static float SIZE = 1.6f;
	public final static float THICKNES = 0.33f;
	public float eyeHeight = SIZE * 0.9f;
	public float viewDistance = Settings.PLAYER_DEFAULT_VIEWDISTANCE;
	protected Scene scene;
	protected boolean onGround = false;
	protected RemotePlayer remotePlayer;
	public EngineObject target;
	
	
	public Player(Scene s, float x, float y, float z) throws LWJGLException{
		super(x,y,z);
		scene = s;
		Mouse.create();
		Keyboard.create();
	}
	
	public void yaw(float dx){
		yaw += dx;
	}
	
	public void pitch(float dx){
		pitch += dx;
	}
	
	public void walk(float dx){
		wvx +=  dx * (float) Math.sin(Math.toRadians(yaw));
		wvz -=  dx * (float) Math.cos(Math.toRadians(yaw));
	}
	
    public void strafeRight(float dx)
    {
    	wvx -= dx * (float) Math.sin(Math.toRadians(yaw-90));
    	wvz += dx * (float) Math.cos(Math.toRadians(yaw-90));
    }
	
	public void look(){
		glRotatef(-pitch, 1.0f, 0.0f, 0.0f);
        glRotatef(yaw, 0.0f, 1.0f, 0.0f);
        glTranslatef(-x, -(y+eyeHeight), -z);               
	}

	@Override
	public void render(EngineObject observer){
		
	}

	@Override
	public void update(float dt) {
		input(dt);
		physics(dt);		
		sendPositionUpdate();
		if(target != null && !inRange(target, viewDistance))
			target = null;
	}
	
	private long lastPositionUpdateNanos=System.nanoTime();
	private void sendPositionUpdate(){
		if(lastPositionUpdateNanos+Settings.PLAYER_MOVEMENT_UPDATE_NANOS<System.nanoTime()){
			Client.client.getScene().getContext().post(new ObjectMoving(-1,x,y,z,vx+wvx,vy+wvy,vz+wvz,pitch,yaw));
			
			lastPositionUpdateNanos = System.nanoTime();
		}
	}
	
	private boolean trySetPosition(float dx, float dy, float dz){
		float nx = x+dx,
			  ny = y+dy,
			  nz = z+dz;
		
		ClientCellBunch cb = ClientCellBunch.cellBunch;
		
		if( !Block.isSolid(cb.getContainingBlock(nx,ny,nz)) &&
			!Block.isSolid(cb.getContainingBlock(nx+THICKNES,ny,nz)) &&
			!Block.isSolid(cb.getContainingBlock(nx-THICKNES,ny,nz)) &&
			!Block.isSolid(cb.getContainingBlock(nx,ny,nz+THICKNES)) &&
			!Block.isSolid(cb.getContainingBlock(nx,ny,nz-THICKNES)) &&
			!Block.isSolid(cb.getContainingBlock(nx,ny+SIZE,nz))){
			
			x = nx;
			y = ny;
			z = nz;
			
			return true;
		}else{
			return false;
		}
	}
	
	public synchronized void physics(float dt){
		vy += Settings.PHYSICS_GRAVITY*dt;
		
		float dx = (vx+wvx)*dt;
		float dy = (vy+wvy)*dt;
		float dz = (vz+wvz)*dt;
		
		//x += dx;
		if(!trySetPosition(dx,0,0)){
			vx = 0;
			wvx = 0;
			//x -= dx;
		}
		
		//y += dy;
		if(!trySetPosition(0,dy,0)){
			vy = 0;
			wvy = 0;
			//y -= dy;
			onGround = true;
		}else{
			onGround = false;
		}
			
		//z += dz;
		if(!trySetPosition(0,0,dz)){
			vz = 0;
			wvz = 0;
			//z -= dz;
		}
	}
	
	public void input(float dt)	{	
		wvx = 0;		wvy = 0;		wvz = 0;
		
		float vwv=0, vsr=0, scale;
		
		if(Client.client.ui.isAnythingOpen)
		{						
			while(Keyboard.next()){
				if(Keyboard.getEventKeyState()){
					int key = Keyboard.getEventKey();
					
					if(key == KEY_ESCAPE)
					{
						Client.client.ui.closeWindow();
					}
					else{								
						Client.client.ui.openWindow.forwardKey(Keyboard.getEventKey(), Keyboard.getEventCharacter());
					}
				}
			}			
			return;
		}
		
		
		if(Keyboard.isKeyDown(KEY_W)){
			vwv += 1;
		}
		if(Keyboard.isKeyDown(KEY_S)){
			vwv -= 1;
		}
		if(Keyboard.isKeyDown(KEY_A)){
			vsr -= 1;
		}
		if(Keyboard.isKeyDown(KEY_D)){
			vsr += 1;
		}
		
		if(vsr != 0 | vwv != 0){
			if(Skill.actualCast!=null){ //without this "if" every frame sends an endcast-packet (~ packets 4000/sec/player)
				Skill.interruptCast();
				Client.client.getScene().getContext().post(new EndCast());
			}
		}
		
		if(Keyboard.isKeyDown(KEY_Q)){
			yaw -=0.1;
		}
		
		if(Keyboard.isKeyDown(KEY_E)){
			yaw +=0.1;
		}
		
		if(Keyboard.isKeyDown(KEY_SPACE)){
			if(onGround){
				vy += jumpSpeed;
				onGround = false;
			}
		}/*
		if(Keyboard.isKeyDown(KEY_LCONTROL)){
			wvy -= walkSpeed;
		}*/
		
		scale = walkSpeed/((float) Math.sqrt(vwv*vwv + vsr*vsr));
		if(vsr!=0) strafeRight(vsr*scale);
		if(vwv!=0) walk(       vwv*scale);
		
		if(Keyboard.isKeyDown(KEY_ESCAPE)){
			Mouse.setGrabbed(false);
		}
		
		if(Mouse.isButtonDown(0)){
			Mouse.setGrabbed(true);
		} 
		
		if(Mouse.isGrabbed()){
			yaw(Mouse.getDX()*mouseSensitivity);
			pitch(Mouse.getDY()*mouseSensitivity);
		}
		
		while(Keyboard.next()){
			if(Keyboard.getEventKeyState()){
				int key = Keyboard.getEventKey();
				
				Action action = KeyBinding.binding.get(key);
				
				if(action!=null)
					action.performOnClient();												
			}
		}
	}

	public RemotePlayer getRemotePlayer() {
		return remotePlayer;
	}

	public void setRemotePlayer(RemotePlayer remotePlayer){
		this.remotePlayer = remotePlayer;
	}
	
	public void say(String msg){
		Client.client.getScene().getContext().post(new TextMessage(remotePlayer.getName(),msg,null));
	}
	
	public void whisper(String msg, String target){
		Client.client.getScene().getContext().post(new TextMessage(remotePlayer.getName(),msg,target));
	}
	
	public String getName(){
		return remotePlayer!=null?remotePlayer.getName():"registering...";
	}

	@Override
	public void update(Server server, float dt) {
		// TODO Auto-generated method stub
		
	}
}
